package com.cmic.rcs.emoji.common.encrypt;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Method;

/**
 * <p>接口出参加密</p>
 * RequestBodyAdvice可以理解为在@RequestBody之前需要进行的 操作，<br/>
 * ResponseBodyAdvice可以理解为在@ResponseBody之后进行的操作;<br/>
 * 所以当接口需要加解密时，在使用@RequestBody接收前台参数之前可以先在RequestBodyAdvice的实现类中进行参数的解密，<br/>
 * 当操作结束需要返回数据时，可以在@ResponseBody之后进入ResponseBodyAdvice的实现类中进行参数的加密。<br/>
 */
//@RestControllerAdvice("com.example.springbootEncrypt.controller")
// 表示com.example.springbootEncrypt.controller此包下的所有响应对象都会经过此拦截器，并对响应体加密
@Slf4j
@RestControllerAdvice
public class EncryptResponseBodyAdapter implements ResponseBodyAdvice<Object> {

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 该方法用于判断当前请求的返回值，是否要执行beforeBodyWrite方法
     *
     * @param methodParameter handler方法的参数对象
     * @param converterType   将会使用到的Http消息转换器类类型
     * @return 返回true则会执行beforeBodyWrite
     */
    @Override
    public boolean supports(MethodParameter methodParameter, @NotNull Class<? extends HttpMessageConverter<?>> converterType) {
        // 默认返回true，对所有响应进行处理，加密逻辑在beforeBodyWrite中判断
        return true;
    }

    /**
     * 在Http消息转换器执转换，之前执行
     *
     * @param body               服务端的响应数据
     * @param methodParameter    handler方法的参数对象
     * @param mediaType          响应的ContentType
     * @param converterType      将会使用到的Http消息转换器类类型
     * @param serverHttpRequest  serverHttpRequest
     * @param serverHttpResponse serverHttpResponse
     * @return 返回 一个自定义的HttpInputMessage，可以为null，表示没有任何响应
     */
    @Override
    @Nullable
    public Object beforeBodyWrite(@Nullable Object body, @NotNull MethodParameter methodParameter, @NotNull MediaType mediaType, @NotNull Class<? extends HttpMessageConverter<?>> converterType, @NotNull ServerHttpRequest serverHttpRequest, @NotNull ServerHttpResponse serverHttpResponse) {
        if (body == null) return null;

        // 获取方法上的EncryptFilter注解
        Method method = methodParameter.getMethod();
        if (method != null) {
            EncryptFilter encryptFilter = method.getAnnotation(EncryptFilter.class);
            // 如果方法上有注解且明确指定不加密响应，则跳过加密
            if (encryptFilter != null && !encryptFilter.encryptResponse()) {
                return body;
            }
        }

        log.info("处理请求地址：{} 的返回值，进行加密处理", serverHttpRequest.getURI());
        //获取请求数据
        String srcData;
        try {
            srcData = objectMapper.writeValueAsString(body);
        } catch (JsonProcessingException e) {
            log.error("响应数据转换为JSON字符串失败：{}", e.getMessage(), e);
            return body;
        }
        log.info("加密前响应body: {}", srcData);
        //加密
        String returnStr = AES128.encrypt(srcData, "8PwWR+bQyGsD/LhFs4rYYg==");
        log.info("加密后响应body: {}", returnStr);

        //添加 encrypt 告诉前端数据已加密
        serverHttpResponse.getHeaders().add("encrypt", "e=a");
        serverHttpResponse.getHeaders().add("Content-Type", "application/json; charset=UTF-8");
        return returnStr;
    }

}